#include <iostream>
#include <mutex>
#include <vector>
#include <cassert>
#include <jsoncpp/json/json.h>

#include "oj_model.hpp"
#include "oj_view.hpp"
#include "../comm/log.hpp"
#include "../comm/httplib.h"
using namespace ns_ojmodel;
using namespace ns_ojview;
using namespace httplib;

// 负载均衡实现
/****************
 * compile_server:
 * 127.0.0.1 8080
 * 127.0.0.1 8081
 * 127.0.0.1 8082
 * 127.0.0.1 8083
 * ..............
 * (类似这样)
 ****************/
// 编译机器结构化
struct Machine
{
public:
    std::string _ip;
    uint16_t _port;
    uint64_t _load;   // 负载因子
    std::mutex *_mtx; // 互斥锁
public:
    Machine() : _ip(""), _port(0), _load(0), _mtx(nullptr)
    {
    }
    ~Machine()
    {
    }

public:
    // 增加负载
    void Enload()
    {
        if (_mtx)
            _mtx->lock();
        ++_load;
        if (_mtx)
            _mtx->unlock();
    }
    // 减少负载
    void Deload()
    {
        if (_mtx)
            _mtx->lock();
        --_load;
        if (_mtx)
            _mtx->unlock();
    }
    //重置负载
    void Resetload()
    {
        if (_mtx)
            _mtx->lock();
        _load = 0;
        if (_mtx)
            _mtx->unlock();
    }
    // 获取负载
    uint64_t Load()
    {
        uint64_t res = 0;
        if (_mtx)
            _mtx->lock();
        res = _load;
        if (_mtx)
            _mtx->unlock();
        return _load;
    }
};

const std::string sevice_machine = "./conf/sevice_machine.conf";

// 负载均衡模块
class LoadBlance
{
private:
    // 所有主机，下标作为主机编号
    std::vector<Machine> _machines;
    // 所有已经上线的主机
    std::vector<int> _online;
    // 所有已经下线的主机
    std::vector<int> _offline;
    std::mutex _mtx;

public:
    LoadBlance()
    {
        assert(LoadMachine(sevice_machine));
        LOG(INFO) << "加载 " << sevice_machine << " 成功"
                  << "\n";
    }
    ~LoadBlance() {}

public:
    // 加载列表
    bool LoadMachine(const std::string &mechine_list)
    {
        std::ifstream in(mechine_list);
        if (!in.is_open())
        {
            LOG(FATAL) << "打开文件：" << mechine_list << "失败"
                       << "\n";
            return false;
        }
        // 打开之后对里面的ip和port进行切割
        std::string line;
        while (std::getline(in, line))
        {
            std::vector<std::string> tokens;
            StringUtil::SplitString(line, &tokens, ":");
            if (tokens.size() != 2)
            {
                LOG(WARNING) << " 切分 " << line << " 失败"
                             << "\n";
                continue;
            }
            Machine m;
            m._ip = tokens[0];
            m._port = atoi(tokens[1].c_str());
            m._load = 0;
            m._mtx = new std::mutex();

            // 从零开始标号并进入
            _online.push_back(_machines.size());
            _machines.push_back(m);
        }
        in.close();
        return true;
    }
    // 平衡选择
    //                输出型参数
    bool BlanceChoice(int *id, Machine **m_out)
    {
        _mtx.lock();
        // 需要选择负载最小的那个主机
        // 先判断是否还能选择
        if (_online.size() == 0)
        {
            LOG(ERROR) << "没有运行中可负载的主机，请进行检查"
                       << "\n";
            return false;
        }
        *id = _online[0];
        *m_out = &_machines[_online[0]];
        uint64_t min_load = _machines[_online[0]].Load();
        for (int i = 1; i < _online.size(); i++)
        {
            uint64_t curr_load = _machines[_online[i]].Load();
            if (min_load > curr_load)
            {
                min_load = curr_load;
                *id = _online[i];
                *m_out = &_machines[_online[i]];
            }
        }
        _mtx.unlock();
        return true;
    }
    // 下线主机
    void OffMachine(int choice)
    {
        _mtx.lock();
        for(auto iter = _online.begin();iter != _online.end();++iter)
        {
            if(*iter == choice)
            {
                //重置负载因子
                _machines[choice].Resetload();
                 _online.erase(iter);
                _offline.push_back(choice);
            }
        }
        _mtx.unlock();
    }
    // 上线主机
    void OnMachine()
    {
        //统一上线
        _mtx.lock();
        _online.insert(_online.end(),_offline.begin(),_offline.end());        
        _offline.erase(_offline.begin(),_offline.end());
        _mtx.unlock();

    }
    void ShowMachines()
    {
        _mtx.lock();
        std::cout << "当前在线主机列表：";
        for(const auto& id:_online)
        {
            std::cout << id << " ";
        }
        std::cout << std::endl;

        std::cout << "当前离线主机列表：";
        for(const auto& id:_offline)
        {
            std::cout << id << " ";
        }
        std::cout << std::endl;
        _mtx.unlock();
    }
};

// 核心控制模块
class Control
{
private:
    Model _model;
    View _view;
    LoadBlance _loadblance;

public:
    bool AllQuestions(std::string *html)
    {
        std::vector<Question> res;
        if (_model.GetAllQuestions(&res))
        {
            _view.LoadHtml(res, html);
            //LOG(INFO) << "LoadHtml" << "\n";
        }
        else
        {
            LOG(ERROR) << "题目列表加载失败"
                       << "\n";
            *html = "题目列表不存在!";
            return false;
        }
        return true;
    }

    bool OneQuestion(const std::string &number, std::string *html)
    {
        Question res;
        if (_model.GetOneQuestion(number, &res))
        {
            _view.LoadHtml(res, html);
        }
        else
        {
            LOG(ERROR) << "题目加载失败"
                       << "\n";
            *html = "指定题目: " + number + " 不存在!";
            return false;
        }
        return true;
    }

    bool Judge(const std::string &number, const std::string &in_json, std::string *out_json)
    {
        // 1.获取题号id,获取问题
        Question q;
        _model.GetOneQuestion(number, &q);
        // 2.对用户输入的in_json进行解析
        Json::Value in_value;
        Json::Reader reader;
        reader.parse(in_json, in_value);
        std::string code = in_value["code"].asString();
        // 2.1拼接用户代码和测试用例代码/形成out_json
        Json::Value compile_value;
        compile_value["code"] = code + q.tail;
        compile_value["input"] = in_value["input"].asString();
        compile_value["cpu_limit"] = q.cpu_limit;
        compile_value["as_limit"] = q.as_limit;
        Json::FastWriter writer;
        std::string compile_string = writer.write(compile_value);
        // 3.选择负载少的主机进行编译
        //   一直请求
        while (true)
        {
            int id = 0;
            Machine *m = nullptr;
            if (!_loadblance.BlanceChoice(&id, &m))
                break; // 没有可选择的编译运行服务器

            // 4.发起http请求，得到结果
            Client cl(m->_ip, m->_port); // 赋给主机的ip和port
            m->Enload();                 // 提升负载
            if (auto ret = cl.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
            {
                // 请求成功
                // 5.返回结果给out_json
                if (ret->status == 200)
                {
                    *out_json = ret->body;
                    m->Deload(); // 使用完了，减少负载
                    LOG(INFO) << "请求编译和运行服务成功..."
                              << "\n";
                    break;
                }
                // 就算没成功也要减少负载
                m->Deload();
            }
            else
            {
                // 请求失败
                LOG(ERROR) << " 当前请求的主机id: " << id << " 详情: " << m->_ip << ":" << m->_port << " 可能已经离线"
                           << "\n";
                _loadblance.OffMachine(id);
                _loadblance.ShowMachines();
            }
        }
        return true;
    }
};